﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BaseManagerImpl.cs" company="Rabid Dog Labs">
//   Rabid Dog Labs
// </copyright>
// <summary>
//   Base Manager Implementation.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using log4net;
using TeamCommunitySolution.Modules.Base.Requests;
using System.Text.RegularExpressions;
using TeamCommunitySolution.Modules.Base.Responses;
using TeamCommunitySolution.Repositories.Base;
using TeamCommunitySolution.Domain.DataLayer.Base;
using TeamCommunitySolution.Modules.Shared;
using TeamRequest = TeamCommunitySolution.Modules.Base.Requests.TeamRequest;

namespace TeamCommunitySolution.Modules.Base
{
    /// <summary>
    /// Base Manager Implementation.
    /// </summary>
    public class BaseManagerImpl : BaseManager
    {
        /// <summary>
        /// Logger instance.
        /// </summary>
        private static readonly ILog _log = LogManager.GetLogger(typeof(BaseManagerImpl));

        /// <summary>
        /// Base Repository reference.
        /// </summary>
        private BaseRepository baseRepo;

        /// <summary>
        /// Prevents a default instance of the <see cref="BaseManagerImpl"/> class from being created. 
        /// Initializes a new instance of the <see cref="BaseManagerImpl"/> class.
        /// </summary>
        private BaseManagerImpl()
        {
        }

        /// <summary>
        /// Creates the instance.
        /// </summary>
        /// <param name="repo">The repository.</param>
        /// <returns>Base Manager.</returns>
        public static BaseManager CreateInstance(BaseRepository repo)
        {
            BaseManagerImpl output = new BaseManagerImpl();
            output.baseRepo = repo;
            return output;
        }

        #region BaseManager Members

        /// <summary>
        /// Registers the user.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Registration Response.</returns>
        public RegistrationResponse RegisterUser(RegistrationRequest request)
        {
            if (request == null)
            {
                throw new InvalidRegistrationRequest("Registration request cannnot be null.");
            }

            if (String.IsNullOrEmpty(request.FirstName))
            {
                throw new InvalidRegistrationRequest("Registration request requires a first name.");
            }

            if (String.IsNullOrEmpty(request.LastName))
            {
                throw new InvalidRegistrationRequest("Registration request requires a last name.");
            }

            if (String.IsNullOrEmpty(request.NickName))
            {
                throw new InvalidRegistrationRequest("Registration request requires a last name.");
            }

            if (String.IsNullOrEmpty(request.Email))
            {
                throw new InvalidRegistrationRequest("Registration request requires an email address.");
            }

            if (!Regex.IsMatch(request.Email, @"^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$"))
            {
                throw new InvalidRegistrationRequest("Registration request requires a valid email address.");
            }

            // if(!PingService.PingServer(request.Email.Split('@')[1]))
            // throw new InvalidRegistrationRequest("Registration request requires a valid email address. It seems yoru email server is not responding to our request.");
            if (String.IsNullOrEmpty(request.Password))
            {
                throw new InvalidRegistrationRequest("Registration request requires a password.");
            }

            if (request.Password.Length < 6)
            {
                throw new InvalidRegistrationRequest("Registration request requires a password with a length great than 6 characters.");
            }

            RegistrationResponse response;
            response = new RegistrationResponse();

            try
            {
                SystemUser user = new SystemUser
                {
                    CreationDate = DateTime.Now,
                    Email = request.Email,
                    FirstName = request.FirstName,
                    LastName = request.LastName,
                    Password = request.Password,
                    NickName = request.NickName
                };

                this.baseRepo.SaveUser(user);

                response.SystemUser = user;
                response.Status = OperationStatus.Success;
            }
            catch (BaseRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete registration for request {0} : {1}", request.Email, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Authenticates the user.
        /// </summary>
        /// <param name="authRequest">The auth request.</param>
        /// <returns>Authentication Response.</returns>
        public AuthenticationResponse AuthenticateUser(AuthenticationRequest authRequest)
        {
            if (authRequest == null)
            {
                throw new InvalidAuthenticationRequest("The Authentication Request cannot be null.");
            }

            if (string.IsNullOrEmpty(authRequest.Username))
            {
                throw new InvalidAuthenticationRequest("The Authentication Request must contain a username.");
            }

            if (string.IsNullOrEmpty(authRequest.Password))
            {
                throw new InvalidAuthenticationRequest("The Authentication Request must contain a password.");
            }

            AuthenticationResponse response = new AuthenticationResponse();

            try
            {
                SystemUser user = this.baseRepo.GetUserByField("Email", authRequest.Username);

                if (user.Password.Equals(authRequest.Password))
                {
                    response.SystemUser = user;
                    response.Status = OperationStatus.Success;
                }
                else
                {
                    response.SystemUser = new SystemUser { Email = authRequest.Username, Password = authRequest.Password };
                    response.Status = OperationStatus.Failure;
                }
            }
            catch (BaseRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Invalid login credentials for {0} : {1}", authRequest.Username, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Creates the team.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>A team response instance.</returns>
        public TeamResponse CreateTeam(TeamRequest request)
        {
            ValidateRequest(request);

            TeamResponse response = new TeamResponse();

            try
            {
                Team team = new Team();
                team.DateFounded = request.DateFounded;
                team.Description = request.Description;
                team.Name = request.Name;
                team.Owner = request.Owner;

                this.baseRepo.SaveTeam(ref team);

                response.Team = team;
                response.Status = OperationStatus.Success;
            }
            catch (BaseRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Could not save team {0} : {1}", request.Name, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Sends the team invite.
        /// </summary>
        /// <param name="inviteRequest">The invite request.</param>
        /// <returns>A team invite response.</returns>
        public TeamInviteResponse SendTeamInvite(TeamInviteRequest inviteRequest)
        {
            if (inviteRequest == null)
            {
                throw new InvalidTeamInviteRequest("The request cannot be null.");
            }

            if (inviteRequest.Team == null)
            {
                throw new InvalidTeamInviteRequest("The request team cannot be null.");
            }

            try
            {
                inviteRequest.Team = this.baseRepo.GetTeamByField("Id", inviteRequest.Team.Id);
            }
            catch (BaseRepositoryException)
            {
                throw new InvalidTeamInviteRequest("The team specified does not exist.");
            }

            if (inviteRequest.UserFrom == null)
            {
                throw new InvalidTeamRequest("The team invite user from cannot be null.");
            }

            try
            {
                inviteRequest.UserFrom = this.baseRepo.GetUserByField("Id", inviteRequest.UserFrom.Id);
            }
            catch (BaseRepositoryException)
            {
                throw new InvalidTeamRequest("The team invite user from does not exist.");
            }

            if (inviteRequest.UserTo == null)
            {
                throw new InvalidTeamRequest("The team invite user to cannot be null.");
            }

            try
            {
                inviteRequest.UserTo = this.baseRepo.GetUserByField("Id", inviteRequest.UserTo.Id);
            }
            catch (BaseRepositoryException)
            {
                throw new InvalidTeamRequest("The team invite user from does not exist.");
            }

            TeamInviteResponse response = new TeamInviteResponse();

            try
            {
                TeamInvite teamInvite = new TeamInvite();

                teamInvite.DateCreated = DateTime.Now;
                teamInvite.PersonalMessage = inviteRequest.PersonalMessage;
                teamInvite.TeamToJoin = inviteRequest.Team;
                teamInvite.UserFrom = inviteRequest.UserFrom;
                teamInvite.UserTo = inviteRequest.UserTo;

                this.baseRepo.SaveTeamInvite(teamInvite);

                response.TeamInvite = teamInvite;
                response.Status = OperationStatus.Success;
            }
            catch (BaseRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format(string.Format("Could not save team invite {0} : {1}", inviteRequest.Team.Id, e.Message));
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Searches the system user.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>A Search Response.</returns>
        public SearchResponse SearchSystemUser(SearchRequest request)
        {
            if (request == null)
            {
                throw new InvalidSearchRequest("The request cannot be null");
            }

            if (string.IsNullOrEmpty(request.SearchString))
            {
                throw new InvalidSearchRequest("The request search string cannot be null or empty.");
            }

            SearchResponse response = new SearchResponse();
            response.ResultList = this.baseRepo.SearchSystemUserByName(request.SearchString);
            response.Status = OperationStatus.Success;

            return response;
        }

        /// <summary>
        /// Gets the system user by username.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>System User Response</returns>
        public SystemUserResponse GetSystemUserByUsername(SystemUserRequest request)
        {
            if (request == null)
            {
                throw new InvalidSystemUserRequest("The request cannot be null");
            }

            if (string.IsNullOrEmpty(request.Username))
            {
                throw new InvalidSystemUserRequest("The request username string cannot be null or empty.");
            }

            SystemUserResponse response = new SystemUserResponse();

            try
            {
                response.SystemUser = baseRepo.GetUserByField("Email", request.Username);
                response.Status = OperationStatus.Success;
            }
            catch (BaseRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete get system user for request {0} : {1}", request.Username, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Gets the system user by username.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>System User Response</returns>
        public SystemUserResponse GetSystemUserByNickname(SystemUserRequest request)
        {
            if (request == null)
            {
                throw new InvalidSystemUserRequest("The request cannot be null");
            }

            if (string.IsNullOrEmpty(request.Username))
            {
                throw new InvalidSystemUserRequest("The request username string cannot be null or empty.");
            }

            SystemUserResponse response = new SystemUserResponse();

            try
            {
                response.SystemUser = baseRepo.GetUserByField("NickName", request.Username);
                response.Status = OperationStatus.Success;
            }
            catch (BaseRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete get system user for request {0} : {1}", request.Username, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Gets the team by id.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Team Response.</returns>
        public TeamResponse GetTeamById(TeamFetchRequest request)
        {
            if (request == null)
            {
                throw new InvalidSystemUserRequest("The request cannot be null");
            }

            if (string.IsNullOrEmpty(request.Id))
            {
                throw new InvalidSystemUserRequest("The request id string cannot be null or empty.");
            }

            TeamResponse response = new TeamResponse();

            try
            {
                response.Team = this.baseRepo.GetTeamByField("Id", request.Id);
                response.Status = OperationStatus.Success;
            }
            catch (BaseRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete get team for request {0} : {1}", request.Id, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Updates the team.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Team Response.</returns>
        public TeamResponse UpdateTeam(TeamUpdateRequest request)
        {
            if (String.IsNullOrEmpty(request.Id))
            {
                throw new InvalidTeamRequest("The team request id cannot be null or empty.");
            }

            ValidateRequest(request);

            TeamResponse response = new TeamResponse();

            try
            {
                Team team = new Team();
                team.DateFounded = request.DateFounded;
                team.Description = request.Description;
                team.Name = request.Name;
                team.Owner = request.Owner;

                this.baseRepo.SaveTeam(ref team);

                response.Team = team;
                response.Status = OperationStatus.Success;
            }
            catch (BaseRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Could not save team {0} : {1}", request.Name, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Gets the team invite by id.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Team Invite Response.</returns>
        public TeamInviteResponse GetTeamInviteById(TeamFetchInviteRequest request)
        {
            if (request == null)
            {
                throw new InvalidSystemUserRequest("The request cannot be null");
            }

            if (string.IsNullOrEmpty(request.Id))
            {
                throw new InvalidSystemUserRequest("The request id string cannot be null or empty.");
            }

            TeamInviteResponse response = new TeamInviteResponse();

            try
            {
                response.TeamInvite = this.baseRepo.GetTeamInviteByField("Id", request.Id);
                response.Status = OperationStatus.Success;
            }
            catch (BaseRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete get team for request {0} : {1}", request.Id, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        /// <summary>
        /// Adds the team member.
        /// </summary>
        /// <param name="request">The request.</param>
        /// <returns>Team Response.</returns>
        public TeamResponse AddTeamMember(TeamMemberAddRequest request)
        {
            if (request == null)
            {
                throw new InvalidTeamInviteRequest("The request cannot be null");
            }

            TeamResponse response = new TeamResponse();

            try
            {
                TeamInvite teamInvite = this.baseRepo.GetTeamInviteByField("Id", request.InviteId);
                Team team = teamInvite.TeamToJoin;

                if (request.IsAccepted)
                {
                    team.MemberList.Add(teamInvite.UserTo);
                    this.baseRepo.SaveTeam(ref team);
                }

                this.baseRepo.DeleteTeamInvite(request.InviteId);

                response.Team = team;
                response.Status = OperationStatus.Success;
            }
            catch (BaseRepositoryException e)
            {
                response.Status = OperationStatus.Failure;
                response.Message = string.Format("Incomplete team member add for request {0} : {1}", request.InviteId, e.Message);
                response.Exception = e;
                _log.Error(response.Message, e);
            }

            return response;
        }

        #endregion

        /// <summary>
        /// Validates the request.
        /// </summary>
        /// <param name="request">The request.</param>
        private void ValidateRequest(TeamRequest request)
        {
            if (request == null)
            {
                throw new InvalidTeamRequest("The team request cannot be null.");
            }

            if (String.IsNullOrEmpty(request.Description))
            {
                throw new InvalidTeamRequest("The team request must contain a description.");
            }

            if (String.IsNullOrEmpty(request.Name))
            {
                throw new InvalidTeamRequest("The team request must contain a name.");
            }

            if (request.DateFounded.Equals(DateTime.MinValue))
            {
                throw new InvalidTeamRequest("The team request must contain a valid date.");
            }

            if (request.Owner == null)
            {
                throw new InvalidTeamRequest("The team request owner cannot be null.");
            }

            try
            {
                request.Owner = this.baseRepo.GetUserByField("Id", request.Owner.Id);
            }
            catch (BaseRepositoryException)
            {
                throw new InvalidTeamRequest("The team owner specified does not exist.");
            }
        }
    }
}
